/*
 * Copyright (C) 2014 luke.
 * 
 * This file is part of Silvie.
 * 
 * Silvie is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Silvie is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Silvie.  If not, see <http://www.gnu.org/licenses/>.
 */

package silvie.resources.chrmodel.chunks;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.vecmath.Point3f;

import utils.DataReader;

/**
 * AbstractChunk specialization for the "shape position IDs" chunk. This chunk
 * contains the shape IDs to map to the positions in the 0x7f05 chunk.
 * 
 * @author luke
 */
public class Chunk0x7f06 extends AbstractChunk {
	private static final int POSITION_IDS_START_OFFSET = 4;
	private static final int TYPE_ID_IS_ZERO = 0;
	private static final int TYPE_SINGLE_ID = 1;
	private static final int TYPE_ID_ARRAY = 2;
	private static final Map<Integer, Point3f> MISSING_SHAPE_POSITIONS = new HashMap<Integer, Point3f>();
	static {
		// JUG.CHR
		MISSING_SHAPE_POSITIONS.put(2142, new Point3f(0, 160.2479f, -2f));
		// TREESPRT.CHR
		MISSING_SHAPE_POSITIONS.put(4355, new Point3f(-1, 176.58351f, 40.20259f));
		// SKELMORT.CHR
		Point3f MISSING_SKELMORT_POSITION = new Point3f(0, 212.47305f, 9.000021f);
		MISSING_SHAPE_POSITIONS.put(8658, MISSING_SKELMORT_POSITION);
		MISSING_SHAPE_POSITIONS.put(8736, MISSING_SKELMORT_POSITION);
		MISSING_SHAPE_POSITIONS.put(8737, MISSING_SKELMORT_POSITION);
		// SGRUNT.CHR
		MISSING_SHAPE_POSITIONS.put(9192, new Point3f(0, 134.20755f, -1.9999887f));
		// CYCLOPS.CHR
		MISSING_SHAPE_POSITIONS.put(12248, new Point3f(0, 384.3817f, 16.355295f));
		// LOBSTER.CHR
		MISSING_SHAPE_POSITIONS.put(16524, new Point3f(0, 79.60092f, 0.82246494f));
	}
	private List<List<Integer>> shapeIds;
	private Map<Integer, Point3f> mappedPositions;

	/**
	 * Chunk0x7f06 constructor.
	 * 
	 * @see AbstractChunk#AbstractChunk(int, byte[])
	 */
	public Chunk0x7f06(int id, byte[] data) {
		super(id, data);
		this.shapeIds = new ArrayList<List<Integer>>();
		this.mappedPositions = new HashMap<Integer, Point3f>();
	}

	@Override
	public int decode() {
		int position = POSITION_IDS_START_OFFSET;
		int length = super.data.length;
		while (position < length) {
			int type = DataReader.readInt(super.data, position);
			switch (type) {
			case TYPE_ID_IS_ZERO:
				this.shapeIds.add(Arrays.asList(0));
				position += DataReader.INT_SIZE;
				break;
			case TYPE_SINGLE_ID:
				this.shapeIds.add(Arrays.asList(DataReader.readInt(super.data, position + DataReader.INT_SIZE)));
				position += 2 * DataReader.INT_SIZE;
				break;
			case TYPE_ID_ARRAY:
				int arraySize = DataReader.readInt(super.data, position + DataReader.INT_SIZE);
				List<Integer> ids = new ArrayList<Integer>(arraySize);
				for (int i = 0; i < arraySize; i++) {
					ids.add(DataReader.readInt(super.data, position + 2 * DataReader.INT_SIZE + DataReader.INT_SIZE * i));
				}
				this.shapeIds.add(ids);
				position += 2 * DataReader.INT_SIZE + DataReader.INT_SIZE * arraySize;
				break;
			}
		}
		return length;
	}

	/**
	 * Maps the IDs from this chunk to the positions of {@code chunk0x7f05}.
	 * 
	 * @param chunk0x7f05 the shape positions chunk.
	 */
	public void mapPositions(Chunk0x7f05 chunk0x7f05) {
		for (int i = 0; i < this.shapeIds.size(); i++) {
			List<Integer> shapeI = this.shapeIds.get(i);
			for (int j = 0; j < shapeI.size(); j++) {
				this.mappedPositions.put(shapeI.get(j), chunk0x7f05.getPositionAt(i));
			}
		}
	}

	/**
	 * Returns the position of the shape whose ID is {@code shapeId}.
	 * {@link #mapPositions(Chunk0x7f05)} must be called first.
	 * 
	 * @param shapeId the shape's ID.
	 * @return the position of the shape {@code shapeId}.
	 */
	public Point3f getShapePosition(int shapeId) {
		Point3f position = this.mappedPositions.get(shapeId);
		if (position != null) {
			return position;
		} else {
			Point3f missingPosition = MISSING_SHAPE_POSITIONS.get(shapeId);
			if (missingPosition != null) {
				return missingPosition;
			}
			return new Point3f(0, 0, 0);
		}
	}
}
